﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tao.OpenGl;
using SceneLib;
using Tao.FreeGlut;

namespace Tarea1
{
    class RasterizationRenderer
    {
        private Scene scene;
        private int width;
        private int height;
        private float[,] zBuffer;

        public RasterizationRenderer(Scene scene, int width, int height)
        {
            this.scene = scene;
            this.width = width;
            this.height = height;
            zBuffer = new float[width + 1, height + 1];     
        }

        public void Render()
        {
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Gl.glOrtho(0, width, 0, height, 1, -1);

            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();

            SetBackgroundColor(scene.Background.Color);

            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
            Gl.glDisable(Gl.GL_DEPTH_TEST);
            Gl.glEnable(Gl.GL_BLEND);
            Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);

            Gl.glBegin(Gl.GL_POINTS);

            //Agregar codigo de dibujado de escena
            CalculateUVW();
            DoRender();
            Gl.glEnd();
            Gl.glFlush();                        
        }

        private void DoRender()
        {
            for (int i = 0; i <= width; i++)
                for (int j = 0; j <= height; j++)
                {
                    zBuffer[i, j] = Single.PositiveInfinity;
                }
            foreach (SceneObject sceneObject in scene.Objects )
            {
                if (sceneObject is SceneTriangle)
                {
                    SceneTriangle triangle = (SceneTriangle)sceneObject;
                    ProcessTriangle(triangle);
                }
                else if (sceneObject is SceneModel)
                {
                    foreach(SceneTriangle triangle in ((SceneModel)sceneObject).Triangles)
                    {
                        List<Vector> vertexAux = new List<Vector>(triangle.Vertex);
                        List<Vector> normalAux = new List<Vector>(triangle.Normal);

                        for (int j = 0; j < triangle.Vertex.Count; j++)
                        {
                            triangle.Vertex[j] = triangle.Vertex[j].Rotate(sceneObject.Rotation).Scale(sceneObject.Scale).Translate(sceneObject.Position);
                            triangle.Normal[j] = triangle.Normal[j].Rotate(sceneObject.Rotation);
                        }
                        ProcessTriangle(triangle);
                        triangle.Vertex = vertexAux;
                        triangle.Normal = normalAux;
                    }
                }
            }
        }

        private void ProcessTriangle(SceneTriangle triangle)
        {
            Vector v1 = triangle.Vertex[0].Rotate(triangle.Rotation).Scale(triangle.Scale).Translate(triangle.Position);
            Vector v2 = triangle.Vertex[1].Rotate(triangle.Rotation).Scale(triangle.Scale).Translate(triangle.Position);
            Vector v3 = triangle.Vertex[2].Rotate(triangle.Rotation).Scale(triangle.Scale).Translate(triangle.Position);
            Vector c1 = VertexShader(triangle.Materials[0], v1, triangle.Normal[0].Rotate(triangle.Rotation));
            Vector c2 = VertexShader(triangle.Materials[1], v2, triangle.Normal[1].Rotate(triangle.Rotation));
            Vector c3 = VertexShader(triangle.Materials[2], v3, triangle.Normal[2].Rotate(triangle.Rotation));

            v1 = ViewingPhase1(v1);
            v2 = ViewingPhase1(v2);
            v3 = ViewingPhase1(v3);           

            Vertex vertex1 = new Vertex
            {
                x = v1.x,
                y = v1.y,
                z = v1.z,
                w = v1.w,                
                u = triangle.U[0],
                v = triangle.V[0],
                Color = c1,
                Material = triangle.Materials[0]
            };

            Vertex vertex2 = new Vertex
            {
                x = v2.x,
                y = v2.y,
                z = v2.z,
                w = v2.w,
                u = triangle.U[1],
                v = triangle.V[1],
                Color = c2,
                Material = triangle.Materials[1]
            };

            Vertex vertex3 = new Vertex
            {
                x = v3.x,
                y = v3.y,
                z = v3.z,
                w = v3.w,
                u = triangle.U[2],
                v = triangle.V[2],
                Color = c3,
                Material = triangle.Materials[2]
            };

            TriangleVertex originalTriangle = new TriangleVertex
            {
                v1 = vertex1,
                v2 = vertex2,
                v3 = vertex3
            };

            List<TriangleVertex> triangles = ClippingTriangle(originalTriangle);

            foreach (TriangleVertex clippedTriangle in triangles)
            {
                v1 = new Vector(clippedTriangle.v1.x, clippedTriangle.v1.y, clippedTriangle.v1.z, clippedTriangle.v1.w);
                v2 = new Vector(clippedTriangle.v2.x, clippedTriangle.v2.y, clippedTriangle.v2.z, clippedTriangle.v2.w);
                v3 = new Vector(clippedTriangle.v3.x, clippedTriangle.v3.y, clippedTriangle.v3.z, clippedTriangle.v3.w);

                v1 = ViewPortPhase(v1);
                v2 = ViewPortPhase(v2);
                v3 = ViewPortPhase(v3);

                clippedTriangle.v1.x = v1.x / v1.w;
                clippedTriangle.v1.y = v1.y / v1.w;
                clippedTriangle.v1.z = v1.z / v1.w;
                
                clippedTriangle.v2.x = v2.x / v2.w;
                clippedTriangle.v2.y = v2.y / v2.w;
                clippedTriangle.v2.z = v2.z / v2.w;
                
                clippedTriangle.v3.x = v3.x / v3.w;
                clippedTriangle.v3.y = v3.y / v3.w;
                clippedTriangle.v3.z = v3.z / v3.w;

                DrawTriangle(clippedTriangle.v1, clippedTriangle.v2, clippedTriangle.v3, false);

            }
        }

        private void CalculateUVW()
        {
            Vector w = scene.Camera.Target - scene.Camera.Position;
            scene.Camera.W = -1 * w.Normalize3();
            scene.Camera.U = Vector.Cross3(scene.Camera.Up, scene.Camera.W);
            scene.Camera.U.Normalize3();
            scene.Camera.V = Vector.Cross3(scene.Camera.W, scene.Camera.U);
            scene.Camera.V.Normalize3();
        }

        private Vector ViewingPhase1(Vector vertex)
        {
            Matrix mCam = GetCameraMatrix();
            Matrix mP = GetPMatrix();
            Matrix mProy = GetProyectionMatrix();
            
            return mProy * (mP * (mCam * vertex));
        }

        private Vector ViewPortPhase(Vector vertex)
        {
            Matrix mViewPort = GetViewPortMatrix();

            return mViewPort * vertex;
        }

        private Matrix GetCameraMatrix()
        {
            Matrix m1 = new Matrix();
            Matrix m2 = new Matrix();
            Vector e = scene.Camera.Position;
            Vector u = scene.Camera.U;
            Vector v = scene.Camera.V;
            Vector w = scene.Camera.W;

            m1.M11 = u.x;
            m1.M12 = u.y;
            m1.M13 = u.z;
            
            m1.M21 = v.x;
            m1.M22 = v.y;
            m1.M23 = v.z;
            
            m1.M31 = w.x;
            m1.M32 = w.y;
            m1.M33 = w.z;

            m1.M44 = 1.0f;

            m2.M11 = 1.0f;
            m2.M22 = 1.0f;
            m2.M33 = 1.0f;
            m2.M44 = 1.0f;

            m2.M14 = -e.x;
            m2.M24 = -e.y;
            m2.M34 = -e.z;
            m2.M44 = 1.0f;
            return m1 * m2;
        }

        private Matrix GetPMatrix()
        {
            float nearClip = -scene.Camera.NearClip;
            float farClip = -scene.Camera.FarClip;
            Matrix m = new Matrix();

            m.M11 = nearClip;
            m.M22 = nearClip;
            m.M33 = nearClip + farClip;
            m.M43 = 1.0f;
            m.M34 = -farClip * nearClip;

            return m;
        }

        private Matrix GetProyectionMatrix()
        {
            Matrix m0 = new Matrix();
            Matrix m1 = new Matrix();
            Matrix m2 = new Matrix();
            float nearClip = -scene.Camera.NearClip;
            float farClip = -scene.Camera.FarClip;
            float top = Convert.ToSingle(Math.Tan(scene.Camera.FieldOfView * Math.PI / 360)) * scene.Camera.NearClip;
            float right = top * width / height;
            float button = -top;
            float left = -right;

            m0.M11 = 1.0f;
            m0.M22 = 1.0f;
            m0.M33 = 1.0f;
            m0.M44 = 1.0f;
            
            m0.M14 = -1.0f;
            m0.M24 = -1.0f;
            m0.M34 = -1.0f;

            m1.M11 = 2.0f / (right - left);
            m1.M22 = 2.0f / (top - button);
            m1.M33 = 2.0f / (farClip - nearClip);
            m1.M44 = 1.0f;

            m2.M11 = 1.0f;
            m2.M22 = 1.0f;
            m2.M33 = 1.0f;
            m2.M44 = 1.0f;

            m2.M14 = -left;
            m2.M24 = -button;
            m2.M34 = -nearClip;
            m2.M44 = 1.0f;
            return m0 * m1 * m2;
        }

        private Matrix GetViewPortMatrix()
        {
            Matrix m0 = new Matrix();
            Matrix m1 = new Matrix();
            Matrix m2 = new Matrix();

            m0.M11 = 1.0f;
            m0.M22 = 1.0f;
            m0.M33 = 1.0f;
            m0.M44 = 1.0f;

            m0.M14 = -0.5f;
            m0.M24 = -0.5f;

            m1.M11 = width / 2;
            m1.M22 = height / 2;
            m1.M33 = 1.0f;
            m1.M44 = 1.0f;

            m2.M11 = 1.0f;
            m2.M22 = 1.0f;
            m2.M33 = 1.0f;
            m2.M44 = 1.0f;
            m2.M14 = 1.0f;
            m2.M24 = 1.0f;
            m2.M34 = 1.0f;

            return m0 * m1 * m2;            
        }

        /// <summary>
        /// Cambia el color del fondo. Los valores de cada componente estan en el rango 0.0 - 1.0
        /// </summary>
        /// <param name="color">Indica el color de fondo (componentes toman valores entre 0.0 y 1.0)</param>
        void SetBackgroundColor(Vector color)
        {
            Gl.glClearColor(color.x, color.y, color.z, color.w);
        }


        /// <summary>
        /// Dibuja un pixel en X,Y con los colores RGB definidos y Alpha (valores entre 0 y 1).
        /// </summary>
        /// <param name="position">Indica el pixel a pintar</param>
        /// <param name="color">Indica el color a pintar</param>
        void DrawPixel(Vector position, Vector color)
        {
            int x = (int)position.x;
            int y = (int)position.y;

            if (x <= width && y <= height && position.x >= 0 && position.y >= 0)
            {
                if (position.z < zBuffer[x, y])
                {
                    if (color.w > 0)
                        color.w = color.w / color.w;
                    Gl.glColor4f(color.x, color.y, color.z, color.w);
                    Gl.glVertex2i(x, y);

                    zBuffer[x, y] = position.z;
                }                
            }
        }

        public void DrawLine(Vertex p1, Vertex p2, bool antialiasing)
        {
            float x0, x1, y0, y1, z0, z1, w0, w1, xf, yf, t, tw, z;
            float u0, u1, v0, v1, u, v;
            int x, y;
            Vector color0, color1;
            SceneMaterial mat0, mat1, mAux;
            List<SceneMaterial> materials = new List<SceneMaterial>();

            x0 = p1.x;
            x1 = p2.x;
            y0 = p1.y;
            y1 = p2.y;
            z0 = p1.z;
            z1 = p2.z;
            w0 = p1.w;
            w1 = p2.w;
            color0 = p1.Color.Copy();
            color1 = p2.Color.Copy();
            mat0 = p1.Material;
            mat1 = p2.Material;
            u0 = p1.u;
            u1 = p2.u;
            v0 = p1.v;
            v1 = p2.v;

            if (Math.Abs(x0 - x1) > Math.Abs(y0 - y1))
            {
                if (x0 > x1)
                {
                    float aux;
                    Vector cAux;

                    aux = x0;
                    x0 = x1;
                    x1 = aux;
                    aux = y0;
                    y0 = y1;
                    y1 = aux;
                    aux = z0;
                    z0 = z1;
                    z1 = aux;
                    aux = w0;
                    w0 = w1;
                    w1 = aux;
                    aux = v0;
                    v0 = v1;
                    v1 = aux;
                    aux = u0;
                    u0 = u1;
                    u1 = aux;
                    mAux = mat0;
                    mat0 = mat1;
                    mat1 = mAux;
                    cAux = color0;
                    color0 = color1;
                    color1 = cAux;
                }

                materials.Add(mat0);
                materials.Add(mat1);

                yf = y0;
                for (x = Convert.ToInt32(x0); x <= x1; x++)
                {
                    t = (x - x0) / (x1 - x0);
                    yf = y0 + t * (y1 - y0);
                    tw = w0 * t / (w1 + t * (w0 - w1));
                    z = (1 - tw) * z0 + tw * z1;
                    u = (1 - tw) * u0 + tw * u1;
                    v = (1 - tw) * v0 + tw * v1;

                    Vector textureColor = GetLineTextureColor(materials, tw, u, v);
                    if (!antialiasing)
                        DrawPixel(new Vector(x, Convert.ToInt32(Math.Round(yf)), z), (((1 - tw) * color0 + tw * color1) * textureColor).Clamp3());
                    else
                    {
                        float d = yf - Convert.ToInt32(yf);
                        Vector ca1 = (((1 - tw) * color0 + tw * color1) * textureColor).Clamp3();
                        Vector ca2 = ca1.Copy();
                        ca1.w = 1 - d;
                        ca2.w = d;
                        DrawPixel(new Vector(x, Convert.ToInt32(Math.Round(yf)), z), ca1);
                        DrawPixel(new Vector(x, Convert.ToInt32(Math.Round(yf + 1)), z), ca2);
                    }
                }
            }
            else
            {
                if (y0 > y1)
                {
                    float aux;
                    Vector cAux;

                    aux = x0;
                    x0 = x1;
                    x1 = aux;
                    aux = y0;
                    y0 = y1;
                    y1 = aux;
                    aux = z0;
                    z0 = z1;
                    z1 = aux;
                    aux = w0;
                    w0 = w1;
                    w1 = aux;
                    cAux = color0;
                    color0 = color1;
                    color1 = cAux;
                    aux = v0;
                    v0 = v1;
                    v1 = aux;
                    aux = u0;
                    u0 = u1;
                    u1 = aux;
                    mAux = mat0;
                    mat0 = mat1;
                    mat1 = mAux;
                }

                materials.Add(mat0);
                materials.Add(mat1);
                                
                for (y = Convert.ToInt32(y0); y <= y1; y++)
                {
                    t = (y - y0) / (y1 - y0);
                    xf = x0 + t * (x1 - x0);
                    tw = w0 * t / (w1 + t * (w0 - w1));
                    z = (1 - tw) * z0 + tw * z1; 
                    u = (1 - tw) * u0 + tw * u1;
                    v = (1 - tw) * v0 + tw * v1;
                    Vector textureColor = GetLineTextureColor(materials, tw, u, v);
                    if (!antialiasing)
                        DrawPixel(new Vector(Convert.ToInt32(Math.Round(xf)), y, z), (((1 - tw) * color0 + tw * color1) * textureColor).Clamp3());
                    else
                    {
                        float d = xf - Convert.ToInt32(xf);
                        Vector ca1 = (((1 - tw) * color0 + tw * color1) * textureColor).Clamp3();
                        Vector ca2 = ca1.Copy();
                        ca1.w = 1 - d;
                        ca2.w = d;
                        DrawPixel(new Vector(Convert.ToInt32(Math.Round(xf)), y, z), ca1);
                        DrawPixel(new Vector(Convert.ToInt32(Math.Round(xf + 1)), y, z), ca2);
                    }
                }
            }
        }

        public void DrawTriangle(Vertex v1, Vertex v2, Vertex v3, bool antialising)
        {
            int xMin, xMax, yMin, yMax;           

            if (v1.x <= v2.x && v1.x <= v3.x)
                xMin = Convert.ToInt32(Math.Floor(v1.x));
            else if (v2.x <= v1.x && v2.x <= v3.x)
                xMin = Convert.ToInt32(Math.Floor(v2.x));
            else
                xMin = Convert.ToInt32(Math.Floor(v3.x));

            if (v1.y <= v2.y && v1.y <= v3.y)
                yMin = Convert.ToInt32(Math.Floor(v1.y));
            else if (v2.y <= v1.y && v2.y <= v3.y)
                yMin = Convert.ToInt32(Math.Floor(v2.y));
            else
                yMin = Convert.ToInt32(Math.Floor(v3.y));

            if (v1.x >= v2.x && v1.x >= v3.x)
                xMax = Convert.ToInt32(Math.Ceiling(v1.x));
            else if (v2.x >= v1.x && v2.x >= v3.x)
                xMax = Convert.ToInt32(Math.Ceiling(v2.x));
            else
                xMax = Convert.ToInt32(Math.Ceiling(v3.x));

            if (v1.y >= v2.y && v1.y >= v3.y)
                yMax = Convert.ToInt32(Math.Ceiling(v1.y));
            else if (v2.y >= v1.y && v2.y >= v3.y)
                yMax = Convert.ToInt32(Math.Ceiling(v2.y));
            else
                yMax = Convert.ToInt32(Math.Ceiling(v3.y));

            if (0 > xMin)
                xMin = 0;
            if (0 > yMin)
                yMin = 0;
            if (width < xMax)
                xMax = width;
            if (height < yMax)
                yMax = height;
            for (int x = xMin; x <= xMax; x++)
                for (int y = yMin; y <= yMax; y++)
                {
                    Vector baricentric = MathUtils.Barycentric2D(x, y, v1.x, v2.x, v3.x, v1.y, v2.y, v3.y);
                    float alfa = baricentric.x;
                    float beta = baricentric.y;
                    float gamma = baricentric.z;

                    if (alfa >= 0 && alfa <= 1 &&
                        beta >= 0 && beta <= 1 &&
                        gamma >= 0 && gamma <= 1)
                    {
                        float alfaw, betaw, gammaw;
                        float d = v2.w * v3.w + v3.w * beta * (v1.w - v2.w) + v2.w * gamma * (v1.w - v3.w);
                        
                        betaw = v1.w * v3.w * beta/ d;
                        gammaw = v1.w * v2.w * gamma/ d;
                        alfaw = 1 - betaw - gammaw;
                        float z = alfaw * v1.z + betaw * v2.z + gammaw * v3.z;
                        float u = alfaw * v1.u + betaw * v2.u + gammaw * v3.u;
                        float v = alfaw * v1.v + betaw * v2.v + gammaw * v3.v;
                        Vector color = alfaw * v1.Color + betaw * v2.Color + gammaw * v3.Color;
                        List<SceneMaterial> materials = new List<SceneMaterial>();

                        materials.Add(v1.Material);
                        materials.Add(v2.Material);
                        materials.Add(v3.Material);
                                               
                        color = color * GetTextureColor(materials, alfaw, betaw, gammaw, u, v);
                        color.Clamp3();
                        DrawPixel(new Vector(x, y, z), color);                        
                    }
                }
            if (antialising)
            {
                DrawLine(v1, v2, antialising);
                DrawLine(v2, v3, antialising);
                DrawLine(v3, v1, antialising);
            }
        }

        public Vector GetTextureColor(List<SceneMaterial> materials, float alfa, float beta, float gamma, float u, float v)
        {
            if (materials[0].TextureImage != null &&
                materials[1].TextureImage != null &&
                materials[2].TextureImage != null)
            {
                Vector colora = GetColorFromTextureVertex(u, v, materials[0]);
                Vector colorb = GetColorFromTextureVertex(u, v, materials[1]);
                Vector colorc = GetColorFromTextureVertex(u, v, materials[2]);

                return (alfa * colora + beta * colorb + gamma * colorc).Clamp3();
            }
            else
                return new Vector(1, 1, 1);
        }

        public Vector GetLineTextureColor(List<SceneMaterial> materials, float t, float u, float v)
        {
            if (materials[0].TextureImage != null &&
                materials[1].TextureImage != null)
            {
                Vector colora = GetColorFromTextureVertex(u, v, materials[0]);
                Vector colorb = GetColorFromTextureVertex(u, v, materials[1]);                

                return ((1 - t) * colora + t * colorb).Clamp3();
            }
            else
                return new Vector(1, 1, 1);
        }

        Vector GetColorFromTextureVertex(float u, float v, SceneMaterial material)
        {
            int i, j;
            int height = material.TextureImage.Height;
            int width = material.TextureImage.Width;
            float deltaU = Convert.ToSingle(u * (width - 1) - Math.Floor(u * (width - 1)));
            float deltaV = Convert.ToSingle(v * (height - 1) - Math.Floor(v * (height - 1)));

            i = Convert.ToInt32(Math.Floor(u * (material.TextureImage.Width - 1)));
            j = Convert.ToInt32(Math.Floor(v * (material.TextureImage.Height - 1)));
            return (1 - deltaU) * (1 - deltaV) * material.GetTexturePixelColor(i, j) +
                                deltaU * (1 - deltaV) * material.GetTexturePixelColor((i + 1) % width, j) +
                                deltaV * (1 - deltaU) * material.GetTexturePixelColor(i, (j + 1) % height) +
                                deltaV * deltaU * material.GetTexturePixelColor((i + 1) % width, (j + 1) % height);
        }

        Vector VertexShader(SceneMaterial material, Vector vertex, Vector normal)
        {
            Vector finalColor = GetAmbientColor(material.Diffuse);

            foreach (SceneLight light in scene.Lights)
            {
                Vector lightColor = new Vector();
                Vector l = light.Position - vertex;

                l.Normalize3();

                float distance = (light.Position - vertex).Magnitude3();

                lightColor = light.Color * 1 / (light.AtenuationQuadratic * distance * distance + light.AtenuationLinear * distance + light.AtenuationConstant);
                finalColor = finalColor + GetDiffuseColor(material.Diffuse, vertex, normal, l, lightColor)
                            + GetPhongColor(material.Specular, material.Shininess, vertex, normal, l, lightColor);
                finalColor.Clamp3(); 
            }

            return finalColor;
        }

        Vector GetAmbientColor(Vector diffuse)
        {
            Vector color = diffuse * scene.Background.AmbientLight;

            color.Clamp3();

            return color;
        }

        private Vector GetDiffuseColor(Vector diffuse, Vector vertex, Vector normal, Vector l, Vector lightColor)
        {
            Vector color = new Vector(0, 0, 0);

            color = color + lightColor * diffuse * Math.Max(0, Vector.Dot3(normal, l));
            color.Clamp3();

            return color;
        }

        private Vector GetPhongColor(Vector specular, float shininess, Vector vertex, Vector normal, Vector l, Vector lightColor)
        {
            Vector e = scene.Camera.Position - vertex;
            Vector color = new Vector(0, 0, 0);

            e.Normalize3();

            Vector h = e + l;

            h.Normalize3();
            color = color + lightColor * specular * Convert.ToSingle(Math.Pow(Vector.Dot3(h, normal), Math.Round(shininess, 2)));
            color.Clamp3();

            return color;
        }

        public List<TriangleVertex> ClippingTriangle(TriangleVertex triangle)
        {
            List<TriangleVertex> triangles = new List<TriangleVertex>();
            float near = -1;
            float epsilon = 0.001f;
            float fa = triangle.v1.z - triangle.v1.w * near;
            float fb = triangle.v2.z - triangle.v2.w * near;
            float fc = triangle.v3.z - triangle.v3.w * near;
            Vertex a = triangle.v1.Copy();
            Vertex b = triangle.v2.Copy();
            Vertex c = triangle.v3.Copy();
            float aux;
            Vertex auxVertex;
            float ta, tb;
            Vertex A, B;

            if (Math.Abs(fa) < epsilon)
                fa = 0.0f;
            if (Math.Abs(fb) < epsilon)
                fb = 0.0f;
            if (Math.Abs(fc) < epsilon)
                fc = 0.0f;

            if (fa <= 0 && fb <= 0 && fc <= 0)
            {
                triangles.Add(triangle);
            }
            else if (fa > 0 && fb > 0 && fc > 0)
            {
            }
            else
            {
                if (fa * fc >= 0)
                {
                    aux = fb;
                    fb = fc;
                    fc = aux;
                    auxVertex = b;
                    b = c;
                    c = auxVertex;
                    aux = fa;
                    fa = fb;
                    fb = aux;
                    auxVertex = a;
                    a = b;
                    b = auxVertex;
                }
                else if (fb * fc >= 0)
                {
                    aux = fa;
                    fa = fc;
                    fc = aux;
                    auxVertex = a;
                    a = c;
                    c = auxVertex;
                    aux = fa;
                    fa = fb;
                    fb = aux;
                    auxVertex = a;
                    a = b;
                    b = auxVertex;
                }

                ta = (a.z - near * a.w) / ((a.z - near * a.w) - (c.z - near * c.z));
                tb = (b.z - near * b.w) / ((b.z - near * b.w) - (c.z - near * c.z));

                A = a + ta * (c - a);
                B = b + tb * (c - b);
                A.Material = a.Material;
                B.Material = b.Material;

                fa = a.z - near * a.w;
                fb = b.z - near * b.w;
                fc = A.z - near * A.w;
                if (Math.Abs(fa) < epsilon)
                    fa = 0.0f;
                if (Math.Abs(fb) < epsilon)
                    fb = 0.0f;
                if (Math.Abs(fc) < epsilon)
                    fc = 0.0f;

                if (fa <= 0 && fb <= 0 && fc <= 0)
                    triangles.Add(new TriangleVertex
                    {
                        v1 = a.Copy(),
                        v2 = b.Copy(),
                        v3 = A.Copy()
                    });

                fa = b.z - near * b.w;
                fb = B.z - near * B.w;
                fc = A.z - near * A.w;
                if (Math.Abs(fa) < epsilon)
                    fa = 0.0f;
                if (Math.Abs(fb) < epsilon)
                    fb = 0.0f;
                if (Math.Abs(fc) < epsilon)
                    fc = 0.0f;

                if (fa <= 0 && fb <= 0 && fc <= 0)                
                    triangles.Add(new TriangleVertex
                    {
                        v1 = b.Copy(),
                        v2 = B.Copy(),
                        v3 = A.Copy()
                    });

                fa = A.z - near * A.w;
                fb = B.z - near * B.w;
                fc = c.z - near * c.w;
                if (Math.Abs(fa) < epsilon)
                    fa = 0.0f;
                if (Math.Abs(fb) < epsilon)
                    fb = 0.0f;
                if (Math.Abs(fc) < epsilon)
                    fc = 0.0f;

                if (fa <= 0 && fb <= 0 && fc <= 0)                 
                    triangles.Add(new TriangleVertex
                    {
                        v1 = A.Copy(),
                        v2 = B.Copy(),
                        v3 = c.Copy()
                    });
            }
            return triangles;
        }
    }   
}
